Ruby 日記 7日目: レキシカルスコープ
次のプログラムを実行するとどうなりますか
code:gold/ex07/main.rb
module M
def refer_const
CONST
end
end
module E
CONST = '010'
end
class D
CONST = "001"
end
class C < D
include E
include M
CONST = '100'
end
c = C.new
p c.refer_const
選択肢:
"001"と表示される
"010"と表示される
"100"と表示される
例外が発生する
解説:
以下より、C.ancestors は [C, M, E, D, Object, Kernel, BasicObject] になる
CクラスはDクラスを継承している
CクラスはEモジュールをインクルードしている
CクラスはMモジュールをインクルードしている
Mモジュールをインクルードしたことで、Cクラスには refer_const がインスタンスメソッドとして定義される。
c.refer_const はMモジュール内の refer_const メソッドを呼ぶ
ところがメソッド内の CONST はMモジュールに定義されていない値なので uninitialized エラーになる
ああそうか、MモジュールでCONSTを定義している必要があるか
例えばMモジュール内でEモジュールをincludeする、など
code:sh
# ruby gold/ex07/main.rb
gold/ex07/main.rb:3:in `refer_const': uninitialized constant M::CONST (NameError)
from gold/ex07/main.rb:22:in `<main>'
答えは「例外が発生する」だね
/icons/hr.icon
スコープについて
スコープ scope
変数・定数・メソッドなどが参照(・代入)可能な、コード上の範囲。
Ruby の定数やグローバル変数はプログラムのどこからでも参照・代入できるため、グローバルスコープと呼ばれる。インスタンス変数はそのインスタンスが self となるような場所であればどこででも参照・代入できる。一方、ローカル変数はローカルスコープという極めて局所的なスコープを持つが、これはコードの見た目だけで決まるレキシカルスコープである。
ソースコード上の見た目だけで決まるスコープ。静的スコープとも言う。
ローカル変数のスコープはレキシカルスコープであるが、インスタンス変数はそのインスタンスが self であるような場所ではどこでも同じ変数が読み書きできるため、レキシカルスコープではない。
たとえば、この例では
code:gold/ex07/lexical_scope01.rb
module M
CONST = 'foo'
class C
def initialize
p CONST
end
end
end
M::C.new
CクラスとCONST定数は同じレキシカルスコープで定義されているのでエラーにならずに参照できる
code:sh
# ruby gold/ex07/lexical_scope01.rb
"foo"
少し書き換えて、この例では
code:gold/ex07/lexical_scope02.rb
module M
CONST = 'foo'
end
class M::C
def initialize
p CONST
end
end
M::C.new
CクラスとCONST定数は同じレキシカルスコープではないので、NameErrorが発生する
code:sh
# ruby gold/ex07/lexical_scope02.rb
gold/ex07/lexical_scope02.rb:7:in `initialize': uninitialized constant M::C::CONST (NameError)
from gold/ex07/lexical_scope02.rb:11:in `new'
from gold/ex07/lexical_scope02.rb:11:in `<main>'
2021-09-05追記:
code:gold/ex07/lexical_scope03.rb
module M
CONST = 'foo'
end
module M
class C
def initialize
p CONST
end
end
end
M::C.new
これ↑であれば、
code:sh
# ruby gold/ex07/lexical_scope03.rb
"foo"
エラーにならない